import ByteBuffer = require("bytebuffer")
import Packet from "./Packet"
import Singleton from "../../framework/scripts/base/Singleton"
import EventMgr from "../../framework/scripts/manager/EventMgr"


export enum NetEvent {
    Open = 'NetOpen',
    Error = 'NetError',
    Close = 'NetClose',
}

interface IHandler {
    cb: Function
    ctx: any
}


export default class NetMgr extends Singleton {
    private _handlers: Map<number, IHandler[]> = new Map()
    private _handlersCall: Map<number, IHandler> = new Map()

    private _socket: WebSocket


    public static get inst() {
        return this.getInstance<NetMgr>()
    }


    public on(packetId: number, cb: Function, ctx?: any) {
        if (this._handlers.has(packetId)) {
            this._handlers.get(packetId).push({ cb, ctx })
        } else {
            this._handlers.set(packetId, [{ cb, ctx }])
        }
    }

    public off(packetId: number, cb: Function, ctx?: any) {
        if (!this._handlers.has(packetId)) {
            console.error(`NetMgr.off packetId:[${packetId}]未注册`)
            return
        }

        const index = this._handlers.get(packetId).findIndex(i => cb === i.cb && i.ctx === ctx)
        index >= 0 && this._handlers.get(packetId).splice(index, 1)
    }

    public clear(ctx: any): void {
        if (!ctx) return

        this._handlers.forEach((handlers) => {
            for (let i = handlers.length - 1; i >= 0; i--) {
                if (handlers[i].ctx == ctx) {
                    handlers.splice(i, 1)
                }
            }
        })
    }


    public connect(wsUrl: string): boolean {
        if (this.isConnect) return true

        this._handlersCall.clear()

        this._socket = new WebSocket(wsUrl)
        this._socket.binaryType = 'arraybuffer'

        this._socket.onopen = (event: Event) => this.openHandler(event)
        this._socket.onmessage = (event: MessageEvent) => this.messageHandler(event)
        this._socket.onerror = (event: Event) => this.errorHandler(event)
        this._socket.onclose = (event: CloseEvent) => this.closeHandler(event)

        return false
    }

    public disConnect() {
        if (this._socket && this._socket.readyState != WebSocket.CLOSING) {
            this._socket.close()
        }
    }

    public send(packet: Packet) {
        if (!this.isConnect) {
            console.error('NetMgr.send 网络未连接或断开')
            return
        }

        const bf = packet.Buffer()
        this._socket.send(bf)
    }

    public sendText(text: string) {
        if (!this.isConnect) {
            console.error('NetMgr.send 网络未连接或断开')
            return
        }

        this._socket.send(text)
    }

    public sendCall(packet: Packet, cb: Function, ctx?: any) {
        if (!this.isConnect) {
            console.error('NetMgr.sendCall 网络未连接或断开')
            return
        }

        this._handlersCall.set(packet.packetId, { cb, ctx })

        const bf = packet.Buffer()
        this._socket.send(bf)
    }

    public get isConnect(): boolean {
        return this._socket && this._socket.readyState == WebSocket.OPEN
    }


    private openHandler(_event: Event): void {
        EventMgr.emit(NetEvent.Open)
    }

    private messageHandler(event: MessageEvent): void {
        this.processRecive(event.data as ArrayBuffer)
    }

    private errorHandler(_event: Event): void {
        EventMgr.emit(NetEvent.Error)
    }

    private closeHandler(_event: CloseEvent): void {
        EventMgr.emit(NetEvent.Close)
    }


    // websocket 是没有粘包的, 但业务包会压缩, 所以会粘在一起
    private processRecive(data: ArrayBuffer): void {
        let bb = new ByteBuffer()
        let bbLen = data.byteLength
        bb.append(data)

        while (bbLen > 4) {
            // 包体长度
            const bodyLen = bb.readUint16(0)

            if (bbLen < 2 + bodyLen) {
                console.error(`NetMgr.processRecive 错误 bbLen:${bbLen}, bodyLen:${bodyLen}`)
                break
            }

            // 包体
            const bodyBuffer = bb.copy(2, 2 + bodyLen) // 2个参数都是 pos
            // 删除1个完整包
            bb = bb.copy(2 + bodyLen, bbLen) // 2个参数都是 pos
            bbLen -= 2 + bodyLen
            // 派发协议
            this.dispatch(bodyBuffer)
        }
    }

    private dispatch(bodyBuffer: ByteBuffer) {
        const packetId = bodyBuffer.readUint16(0)
        const replyId = bodyBuffer.readUint16(2)
        const packetBuffer = bodyBuffer.slice(4)

        if (this._handlersCall.has(replyId)) {
            const { cb, ctx } = this._handlersCall.get(replyId)
            this._handlersCall.delete(replyId)

            const packet = new Packet(packetBuffer)
            cb.apply(ctx, [packet])

            return
        }

        if (!this._handlers.has(packetId)) {
            console.error(`NetMgr.dispatch packetId:[${packetId}]未注册`)
            return
        }

        this._handlers.get(packetId).forEach(({ cb, ctx }) => {
            const packet = new Packet(packetBuffer)
            cb.apply(ctx, [packetId, packet])
        })
    }
}
